java快速学习速查(7)[进阶篇]

JBDC

JDBC:java语言连接数据库,通过java语言操作数据库中的数据。JDBC(Java Database Connectivity)是sun公司指定的一套标准规范,由很多的类和接口组成,在java.sql.*包下。

JDBC本质上是sun公司提供的一套接口,接口的实现类由数据库厂商提供。

  • sum公司定义的一套操作所有关系型数据库的规范,即接口。
  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包。
  • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

这玩意的存在,为世界上所有的关系型数据库提供了统一的访问方式。是编程中访问数据库的标准规范。

各数据库厂商使用相同的接口,java代码不需要针对不同的数据库分别开发。
可以随时替换叠层数据库,访问数据库的java代码基本不变,以后编写操作数据库的代码只需要面向JDBC(接口),操作哪个关系型数据库就需要导入该数据库的驱动jar包,需要操作mysql数据库,就需要在项目中导入mysql数据库的驱动包。

java访问数据库(流程和对应的方法)

在java中访问数据库有几个步骤:
1.加载并注册数据库驱动
2.建立数据连接(URL书写,用户名,密码)

3.创建Statement
4.执行对数据库的增删改查操作
5.结果集处理

对应以上步骤的方法名是:

  • 加载并注册数据库驱动:Class.forName(“com.mysql.jdbc.Driver”);
  • 建立数据连接:Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/db_name”, “username”, “password”);
  • 创建Statement:Statement stmt = conn.createStatement();
  • 执行对数据库的增删改查操作:int rowsAffected = stmt.executeUpdate(“INSERT INTO users (name, age) VALUES (‘John Doe’, 30)”);
  • 结果集处理:ResultSet rs = stmt.executeQuery(“SELECT * FROM users”);
  • 关闭连接:rs.close(); stmt.close(); conn.close();

我们在进行数据库和java连接前,请一定注意将对应版本的驱动包导入项目中。
(mysql-connector-java-5.1.37-bin.jar)添加到工程中的classpath里面(右键add as library),我们可以直接拷贝jar包。

连接方式上,我们可以直接在对应的行为类中进行连接,也可以在工具类中进行连接。
直接连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.iweb.test
import java.sql.Connection;
import java.sql.DriverManager;
public class Test1 {
   public static void main(String[] args) throws  Exception {
       // 加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       // 驱动管理类获得连接对象
       // url:连接的数据库的地址
       // user: 连接数据库的账号
       // pass: 连接数据库的密码
       String url="jdbc:mysql://localhost:3306/mydb";
       String user="root";
       String pass="root";
       // DriverManager驱动管理类获得了一个数据库连接对象
       // Connection 接口负责java程序与数据库之间的连接
       Connection cn=DriverManager.getConnection(url,user,pass);
       System.out.println(cn);
  }
}

用其他的配置文件保存数据库配置信息(更推荐)

配置文件生命在工程的src目录下【jdbc.properties】
说明:使用配置文件的方式保存数据库配置信息,在代码中加载配置文件。
使用配置文件的好处:
1.实现了代码和数据分离,如果需要修改数据库配置信息,直接在配置文中修改,不需要深入代码。
2.如果修改了配置信息,省去重新编译的过程。

配置文件生命在工程的src目录下【jdbc.properties】

1
2
3
url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8
user=root
pass=root

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.iweb.test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
public class Test2 {
   public static void main(String[] args)  throws  Exception{
       // 加载jdbc.properties文件
       InputStream resourceAsStream
=Test2.class.getClassLoader().getResourceAsStream("jdbc.properties");
       // Properties 继承于 HashTable,主要用于读取配置文件
       Properties pros=new Properties();
       // 加载文本信息到属性集
       pros.load(resourceAsStream);
       // 读取配置信息,依据KEY获取Value
       String url = pros.getProperty("url");
       String user = pros.getProperty("user");
       String pass = pros.getProperty("pass");
       // 1.加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       // 2.驱动管理类获得一个连接对象
       Connection cn = DriverManager.getConnection(url, user, pass);
       System.out.println(cn);
  }
}

statement

操作和访问数据库

数据库连接用于向数据库服务器发送命令和SQL语句,并接受数据库服务器返回的结果,其实就一个数据库连接就是一个socket连接。
在java.sql包中有3各接口,分别定了对数据库的调用的不同方式

  • Statement:用于执行静态SQL语句并返回它所生成的结果的对象。
  • PrepatedStatement:SQL语句预编译对象,可以使用对象多次高效的执行该SQL语句。

statement
createStatement()用于创建statement对象,我们通过statement对象来完成对数据库的增删改查操
作。其实就是通过statement对象来将SQL语句发送给数据库,然后执行SQL语句。
该方法的具体的使用方法为:

1
Statement st = cn.createStatement();

此处注意一下啊:
statement只能执行静态SQL语句,会有安全隐患,这个会在PrepatedStatement:SQL接口的时候讲解。(statement不推荐使用)

执行增删改查

执行对数据库的操作主要是执行statement中的方法。
执行给定的SQL语句,语句为insert、update、和delete语句,返回值是语句响应的行数。

1
int executeUpdate(String sql)

实际实例:(DML操作)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.iweb.test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;
public class Test2 {2).查询数据
执行给定的查询的SQL语句,该语句返回ResultSet对象。
ResultSet(结果集对象)封装了SQL查询语句的结果
1.通过列名
   public static void main(String[] args)  throws  Exception{
       // 加载jdbc.properties文件
       InputStream resourceAsStream
=Test2.class.getClassLoader().getResourceAsStream("jdbc.properties");
       // Properties 继承于 HashTable,主要用于读取配置文件
       Properties pros=new Properties();
       // 加载文本信息到属性集
       pros.load(resourceAsStream);
       // 读取配置信息,依据KEY获取Value
       String url = pros.getProperty("url");
       String user = pros.getProperty("user");
       String pass = pros.getProperty("pass");
       // 1.加载驱动
       Class.forName("com.mysql.jdbc.Driver");
       // 2.驱动管理类获得一个连接对象
       Connection cn = DriverManager.getConnection(url, user, pass);
       // 3.通过Connection对象获得数据库操作工具(statement)
       Statement st = cn.createStatement();
       // 4.数据库操作工具(statement)来执行SQL语句
       // st.executeQuery()用于执行查询操作 DQL
       // st.executeUpdate() 用于执行增删改操作 DML
       // 新增
       // String sql="insert into emp
values(null,'1012','wangjie','男',55,'4313','2021-2-2',10,3000)";
       // 修改
       // String sql="update emp set name='王杰',age=58 where id=30";
       // 删除
       String sql="delete from emp where id=30";
       int result = st.executeUpdate(sql);
       if(result>0){
           System.out.println("删除成功");
      }else{
           System.out.println("删除失败");
      }
       st.close();
       cn.close();
  }
}

查询数据操作

执行给定的查询的SQL语句,该语句返回ResultSet对象。

1
ResultSet executeQuery(sql)

ResultSet(结果集对象)封装了SQL查询语句的结果

通过列名查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.iweb.test;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class Test3 {
   public static void main(String[] args)  throws  Exception {
       Class.forName("com.mysql.jdbc.Driver");
       InputStream is =
Test3.class.getClassLoader().getResourceAsStream("jdbc.properties");
       Properties prop = new Properties();
       prop.load(is);
       String url = prop.getProperty("url");
       String user = prop.getProperty("user");
       String pass = prop.getProperty("pass");
       Connection cn = DriverManager.getConnection(url, user, pass);
       // Statement st 操作数据库执行的工具
       Statement st = cn.createStatement();
       // 执行查询
       String sql="select * from emp";
       // 获得SQL查询语句的结果
       ResultSet rs = st.executeQuery(sql);
       // next判断是否还有下行数据,如果还有下一条数据,则返回true,否则false
       // 循环结果集
       while(rs.next()){
           // 通过字段名
           int id = rs.getInt("id");
           String worknumber = rs.getString("worknumber");
           String name = rs.getString("name");
           String sex = rs.getString("sex");
           int age = rs.getInt("age");
           String card = rs.getString("card");
           Date hiredate = rs.getDate("hiredate");
           int dno = rs.getInt("dno");
           int sal = rs.getInt("sal");
         
 System.out.println("id:"+id+",worknumber:"+worknumber+",name:"+name+",sex:"+sex
+",age:"+age+",card:"+card+",hiredate:"+hiredate+",dno:"+dno+",sal:"+sal);
      }
       // 释放资源
       rs.close();
       st.close();
       cn.close();
  }
}


使用下标获取表中列的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.iweb.test;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class Test4 {
   public static void main(String[] args)  throws  Exception {
       Class.forName("com.mysql.jdbc.Driver");
       InputStream is =
Test4.class.getClassLoader().getResourceAsStream("jdbc.properties");
       Properties prop = new Properties();
       prop.load(is);
       String url = prop.getProperty("url");
       String user = prop.getProperty("user");
       String pass = prop.getProperty("pass");
       Connection cn = DriverManager.getConnection(url, user, pass);
       // Statement st 操作数据库执行的工具
       Statement st = cn.createStatement();
       // 准备一条SQL语句
       String sql="select * from emp where id=20";
       // 执行SQL语句
       ResultSet rs = st.executeQuery(sql);
       if(rs.next()){
           // 通过下标
           int id = rs.getInt(1);
           String number = rs.getString(2);
           String name = rs.getString(3);
         
 System.out.println("id:"+id+"\t"+"number:"+number+"\t"+"name:"+name);
      }
       rs.close();
       st.close();
       cn.close();
}
}

关于Result接口的注意事项
1)注意类型不要获取错了
2)使用完毕要关闭结果集ResultSet,再关闭Statement,再关闭Connection(关闭原则)

综合案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.iweb.test;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class UserLogin {
   public static void main(String[] args) {
       // 用户从控制台输入账号和密码去登录
       Scanner sc =new Scanner(System.in);
       System.out.println("请输入用户名");
       String username = sc.nextLine();
       System.out.println("请输入密码");
       String password = sc.nextLine();
       // 调用登录方法
       login(username,password);
  }
   /**
    * 登录功能
    * @param username 登录账号
    * @param password 登录密码
    */
   public static void login(String username,String password){
       Connection cn =null;
       Statement st =null;
       ResultSet rs=null;
       try {
           //1. 注册并加载驱动
           Class.forName("com.mysql.jdbc.Driver");
           //2.读取属性文件
           InputStream is =
UserLogin.class.getClassLoader().getResourceAsStream("jdbc.properties");
           Properties prop = new Properties();
           prop.load(is);
           String url = prop.getProperty("url");
           String user = prop.getProperty("user");
           String pass = prop.getProperty("pass");
           cn = DriverManager.getConnection(url, user, pass);
           //3.连接Connection获得操作工具
           st = cn.createStatement();
           String sql="SELECT * FROM userinfo WHERE username='"+username+"' AND
PASSWORD='"+password+"'";
           System.out.println(sql);
           rs = st.executeQuery(sql);
           if (rs.next()) {
               System.out.println("欢迎"+username+"登录本系统!!");
          }else{
               System.out.println("账号或密码错误,请重新登录!!");
          }
      } catch (Exception e) {
           e.printStackTrace();
      }finally {
           try {
               rs.close();
               st.close();
               cn.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
  }
}

SQL注入问题(有面试题的喔)

当我们输出密码,发现账号和密码不正确竟然登录成功。
例如:执行以下结果机器现象

请输入用户名
admin
请输入密码
a’ or ‘1’=’1
SELECT * FROM userinfo WHERE username=’admin’ AND PASSWORD=’a’ or ‘1’=’1’
欢迎admin登录本系统!!

select * from userinfo where true 查询所有记录

以上案例出现的问题是:
我们让用户输入的密码和SQL语句进行字符串拼接,用户输入的内容作为SQL语句语法的一部分,改变
了原有SQL真正的意义,以上问题称为SQL注入,要解决SQL注入就不能让用户输入的密码和我们SQL
语句进行简单的字符串拼接。

使用Statement操作数据库表存在的弊端:

  • 问题一:存在拼串操作,繁琐
  • 问题二:存在SQL注入问题

对于java而言,要防范SQL注入,只要用 PreparedStatement(从Statement扩展而来)取代
Statement就可以了。

PreparedStatement

PreparedStatement也叫做预处理对象,它是Statement的子接口,两者功能相似,但是如果多次执行SQL语句时PreparedStatement效率会更高,它还可以给SQL语句传递参数,避免SQL注入问题。
预编译:将SQL语句发送给数据库预编译,PreparedStatement会引用这预编译后的结果,可以多次传
入不同的参数给PreparedStatement对象并执行,减少SQL编译次数,提高效率。

使用PreparedStatement的步骤
1). 编写SQL语句时,未知参数用?占位

1
String sql="SELECT * FROM userinfo WHERE username=? AND PASSWORD=?";

2).通过链接对象获得PreparedStatement对象
3).设置实际参数的值 setXXX(占位符的位置,赋予真实的值)
4).执行带参数的SQL语句
5).关闭资源

在使用数据库语言时请在SQL中先行写好之后移植过来

案例(登录操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class UserLogin {
   public static void main(String[] args) {
       // 用户从控制台输入账号和密码去登录
       Scanner sc =new Scanner(System.in);
       System.out.println("请输入用户名");
       String username = sc.nextLine();
       System.out.println("请输入密码");
       String password = sc.nextLine();
       // 调用登录方法
       login(username,password);
  }
   /**
    * 登录功能
    * @param username 登录账号
    * @param password 登录密码
    */
   public static void login(String username,String password){
       Connection cn =null;
       ResultSet rs=null;
       PreparedStatement ps=null;
       try {
           //1. 注册并加载驱动
           Class.forName("com.mysql.jdbc.Driver");
           //2.读取属性文件
           InputStream is =
UserLogin.class.getClassLoader().getResourceAsStream("jdbc.properties");
           Properties prop = new Properties();
           prop.load(is);
           String url = prop.getProperty("url");
           String user = prop.getProperty("user");
           String pass = prop.getProperty("pass");
           cn = DriverManager.getConnection(url, user, pass);
           // 编写SQL语句时,未知参数用?占位
           String sql="SELECT * FROM userinfo WHERE username=? AND PASSWORD=?";
           // 通过连接获得预处理对象PreparedStatement
           ps = cn.prepareStatement(sql);
           // 设置实际参数的值 setXXX(占位符的位置,赋予真实的值)
           ps.setString(1,username);
           ps.setString(2,password);
           // 执行带参数的SQL语句
           rs = ps.executeQuery();
           if(rs.next()){
               System.out.println("登录成功:"+username);
          }else{
               System.out.println("账号或密码错误");
          }
      } catch (Exception e) {
           e.printStackTrace();
      }finally {
           try {
               rs.close();
               ps.close();
               cn.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
  }
}


实际案例(模糊查询)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class EmpLikeTest {
   public static void main(String[] args) {
       Scanner sc =new Scanner(System.in);
       System.out.println("请输入要查询的职员姓名");
       String name = sc.next();
       Connection cn =null;
       PreparedStatement ps=null;
       ResultSet rs=null;
       try {
           Class.forName("com.mysql.jdbc.Driver");
           InputStream is = Test4.class.getClassLoader().
                   getResourceAsStream("jdbc.properties");
           Properties prop = new Properties();
           prop.load(is);
           String url = prop.getProperty("url");
           String user = prop.getProperty("user");
           String pass = prop.getProperty("pass");
           cn = DriverManager.getConnection(url, user, pass);
           // 未知参数?占位
           String sql="select * from emp where name like ?";
           // 获得预编译对象
           ps = cn.prepareStatement(sql);
           // 设置参数
           ps.setString(1,"%"+name+"%");
           // 返回结果集
           rs=ps.executeQuery();
           // 循环结果集
           while(rs.next()){
               System.out.println(rs.getString("name")+"\t"+
                       rs.getInt("age")+"\t"+
                       rs.getDate("hiredate"));
          }
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           try {
               rs.close();
               ps.close();
               cn.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
  }
}


注意
使用jdbc进行模糊查询的时候”% _”不能写在SQL里面,写在执行的SQL有时候语法错误,所有我们要写setXXX()里面。
表与类的关系整张表看作是一个类,表的一行数据是类的一个实例对象。
1).查询一条数据,封装成一个Emp对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class TestById {
   public static void main(String[] args) {
       Connection cn=null;
       PreparedStatement ps=null;
       ResultSet rs=null;
       Emp emp=null;
       try {
           Class.forName("com.mysql.jdbc.Driver");
           InputStream is =
Test3.class.getClassLoader().getResourceAsStream("jdbc.properties");
           Properties prop = new Properties();
           prop.load(is);
           String url = prop.getProperty("url");
           String user = prop.getProperty("user");
           String pass = prop.getProperty("pass");
           cn = DriverManager.getConnection(url, user, pass);
           String sql="select * from emp where id=?";

           // 预编译对象
           ps=cn.prepareStatement(sql);
           // 赋值操作
           ps.setInt(1,20);
           // 执行
           rs = ps.executeQuery();
           if(rs.next()){
               // 将查询的一行数据封装为Emp对象
                emp=new Emp(rs.getInt("id"),
                       rs.getString("worknumber"),
                       rs.getString("name"),
                       rs.getString("sex"),
                       rs.getInt("age"),
                       rs.getString("card"),
                       rs.getDate("hiredate"),
                       rs.getInt("sal"),
                       rs.getInt("dno")
              );
          }
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           try {
               rs.close();
               ps.close();
               cn.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
       System.out.println(emp.toString());
  }
}


2).将多条数据封装称集合 List ,集合中每个元素都是一个Emp对象,查询所有的职员类,封装成 List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class TestSelectAll {
   public static void main(String[] args) {
       Connection cn=null;
       PreparedStatement ps=null;
       ResultSet rs=null;
       List<Emp> list =new ArrayList<Emp>();
       try {
           Class.forName("com.mysql.jdbc.Driver");
           InputStream is =
Test3.class.getClassLoader().getResourceAsStream("jdbc.properties");
           Properties prop = new Properties();
           prop.load(is);
           String url = prop.getProperty("url");
           String user = prop.getProperty("user");
           String pass = prop.getProperty("pass");
           cn = DriverManager.getConnection(url, user, pass);
           // 预处理SQL语句
           ps=cn.prepareStatement("select * from emp");
           // 没有?要赋值 
           // 执行返回结果集
           rs = ps.executeQuery();
           while(rs.next()){
               // 每一行数据封装成Emp对象
              Emp emp=new Emp(rs.getInt("id"),
                       rs.getString("worknumber"),
                       rs.getString("name"),
                       rs.getString("sex"),
                       rs.getInt("age"),
                       rs.getString("card"),
                       rs.getDate("hiredate"),
                       rs.getInt("sal"),
                       rs.getInt("dno")
              );
               // 将封装的Emp对象添加到集合中
               list.add(emp);
          }
      }catch (Exception e){
           e.printStackTrace();
      } finally {
           try {
               rs.close();
               ps.close();
               cn.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
       for(Emp e : list){
           System.out.println(e.toString());
      }
  }
}

增删改操作

使用prepareStatement进行新增操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.iweb.test;

import java.io.InputStream;
import java.sql.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Properties;

public class TestEmpInsert {

public static void main(String[] args) {
Connection cn =null;
PreparedStatement ps=null;
ResultSet rs=null;
try{
Class.forName("com.mysql.jdbc.Driver");
InputStream is = Test4.class.getClassLoader().
getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String pass = prop.getProperty("pass");
cn = DriverManager.getConnection(url, user, pass);
// 获得预编译对象
String sql="INSERT INTO emp VALUES(NULL,?,?,?,?,?,?,?,?)";
ps=cn.prepareStatement(sql);
// 赋值
ps.setString(1,"1112");
ps.setString(2,"zhouxixi");
ps.setString(3,"男");
ps.setInt(4,58);
ps.setString(5,"4333");
// 创建java.util.Date对象转为java.sql.Date对象(没有时间部分)
//ps.setDate(6,new java.sql.Date(new java.util.Date().getTime()));
// java8引入的新的日期时间API
// LocalDate.now() 获得当前日期
// LocalTime.now() 获得当前时间
// LocalDateTime.now 获得当前时间和日期
ps.setDate(6, java.sql.Date.valueOf(LocalDate.now()));
ps.setInt(7,40);
ps.setInt(8,5000);
// 执行
int result=ps.executeUpdate();
if(result>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
ps.close();
cn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}

使用prepareStatement进行修改操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TestEmpUpdate {
public static void main(String[] args) {
Connection cn =null;
PreparedStatement ps=null;
ResultSet rs=null;
try{
Class.forName("com.mysql.jdbc.Driver");
InputStream is = Test4.class.getClassLoader().
getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String pass = prop.getProperty("pass");
cn = DriverManager.getConnection(url, user, pass);
String sql="update emp set name=?,age=? where id=?";
ps=cn.prepareStatement(sql);
ps.setString(1,"肉丝");
ps.setInt(2,28);
ps.setInt(3,24);
if(ps.executeUpdate()>0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
ps.close();
cn.close();
}catch (Exception e){
e.printStackTrace();
}
}

}
}

使用prepareStatement进行删除操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TestEmpDelete {

public static void main(String[] args) {
Connection cn =null;
PreparedStatement ps=null;
ResultSet rs=null;
try{
Class.forName("com.mysql.jdbc.Driver");
InputStream is = Test4.class.getClassLoader().
getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String pass = prop.getProperty("pass");
cn = DriverManager.getConnection(url, user, pass);
String sql="delete from emp where id=?";
ps=cn.prepareStatement(sql);
ps.setInt(1,20);
if(ps.executeUpdate()>0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
ps.close();
cn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}

数据库工具类

需求:上面写的代码中出现了很多重复代码,可以把这些共同的代码抽取出来。

开始打包操作……呜噜噜

创建类JdbcUtils(其实这个很重要的,到时候我会发蓝图,所以只是简单总结一下)

JdbcUtils类的作用是封装数据库连接,以及释放数据库资源。

  • 驱动程序、用户名、密码、URL定义为静态常量。
  • 注册驱动使用static静态代码块实现,只需要加载一次。
  • 提供获取数据库连接对象的静态方法。
  • 释放系统资料的方法

包含三个方法:

1)用户名、密码、URL、驱动类定义为静态常量
2)获得数据库连接 getConnecation()
3)关闭所有打开的资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.iweb.utils;

import com.iweb.test.Test4;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
// 1)用户名、密码、URL、驱动类定义为静态常量
private static String url;
private static String user;
private static String password;
private static String driver;

// 文件的读取,只需要读取一次即可拿到这些值,静态代码块
static{
try {
InputStream is = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
prop.load(is);
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("pass");
driver=prop.getProperty("driverClass");
// 加载驱动
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}


// 2)获得数据库连接 getConnecation()
public static Connection getConnection() throws Exception{
return DriverManager.getConnection(url,user,password);
}


// 3)关闭所有打开的资源 关闭顺序 ResultSet-->Statement-->Connection
public static void close(ResultSet rs, Statement st, Connection cn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(cn!=null){
try {
cn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

public static void close(Statement st, Connection cn){
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(cn!=null){
try {
cn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
public class TestJdbcUtils {
public static void main(String[] args) {
/* 删除
int reuslt= deleteEmp(21);
if(reuslt>0){
System.out.println("删除成功!");
}else{
System.out.println("删除失败!");
}*/
/* 添加
Emp emp=new Emp();
emp.setWorkNumber("1211");
emp.setName("zhouhaha");
emp.setSex("男");
emp.setAge(28);
emp.setCard("4333");
emp.setSal(2800);
emp.setDno(50);
int result = saveEmp(emp);
if(result>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}*/
/* Emp emp=new Emp();
emp.setName("周哈");
emp.setAge(32);
emp.setId(33);
System.out.println(editEmp(emp)>0?"修改成功":"修改失败");*/

/*Emp emp = findById(22);
System.out.println(emp);*/

List<Emp> list = findAll();
if(!list.isEmpty()){
for (Emp e: list) {
System.out.println(e);
}
}

}

public static List<Emp> findAll(){
Connection cn=null;
PreparedStatement ps=null;
ResultSet rs=null;
List<Emp> list=new ArrayList<Emp>();
// 获得连接对象
try {
cn = JdbcUtils.getConnection();
ps=cn.prepareStatement("select * from emp");
rs=ps.executeQuery();
while(rs.next()){
// 将一行数据封装成一个Emp对象
Emp emp=new Emp();
emp.setName(rs.getString("name"));
emp.setHiredate(rs.getDate("hiredate"));
emp.setSal(rs.getInt("sal"));
// 将对象添加到集合
list.add(emp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//调用封装好的close方法来关闭
JdbcUtils.close(rs,ps,cn);
}
return list;
}

public static Emp findById(int id){
Connection cn=null;
PreparedStatement ps=null;
ResultSet rs=null;
Emp emp=null;
// 获得连接对象
try {
cn = JdbcUtils.getConnection();
ps=cn.prepareStatement("select * from emp where id=?");
ps.setInt(1,id);
rs=ps.executeQuery();
while(rs.next()){
// 将一行数据封装成一个Emp对象
emp=new Emp();
emp.setName(rs.getString("name"));
emp.setHiredate(rs.getDate("hiredate"));
emp.setSal(rs.getInt("sal"));
return emp;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//调用封装好的close方法来关闭
JdbcUtils.close(rs,ps,cn);
}
return emp;
}


public static int editEmp(Emp emp){
Connection cn=null;
PreparedStatement ps=null;
// 获得连接对象
try {
cn = JdbcUtils.getConnection();
ps = cn.prepareStatement("update emp set name=?,age=? where id=?");
ps.setString(1,emp.getName());
ps.setInt(2,emp.getAge());
ps.setInt(3,emp.getId());
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//调用封装好的close方法来关闭
JdbcUtils.close(ps,cn);
}
return 0;
}

public static int saveEmp(Emp emp){
Connection cn=null;
PreparedStatement ps=null;
// 获得连接对象
try {
cn = JdbcUtils.getConnection();
ps = cn.prepareStatement("insert into emp values(null,?,?,?,?,?,?,?,?)");
ps.setString(1,emp.getWorkNumber());
ps.setString(2,emp.getName());
ps.setString(3,emp.getSex());
ps.setInt(4,emp.getAge());
ps.setString(5,emp.getCard());
ps.setDate(6, java.sql.Date.valueOf(LocalDate.now()));
ps.setInt(7,emp.getDno());
ps.setInt(8,emp.getSal());
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//调用封装好的close方法来关闭
JdbcUtils.close(ps,cn);
}
return 0;
}

public static int deleteEmp(int id){
Connection cn=null;
PreparedStatement ps=null;
// 获得连接对象
try {
cn = JdbcUtils.getConnection();
ps = cn.prepareStatement("delete from emp where id=?");
ps.setInt(1,id);
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//调用封装好的close方法来关闭
JdbcUtils.close(ps,cn);
}
return 0;
}
}

数据库事务

事物是一组操作的集合,事务会把所有的操作作为一个整体一起向系统提交或撤销请求,这些操作要么同时成功,要么同时失败。

JDBC事务处理

1.数据一旦提交,就不可回滚。

2.数据什么时候意味着提交?

  • 当一个连接对象被创建时,默认情况下是自动提交事务,每次执行一个SQL语句时,如果执行成功就会向数据自动提交,而不能回滚。
  • 关闭数据库连接,数据就会自动的提交。如果多个操作,每个操作使用的都是自己单独的连接,则无法保证。事务,即同一个事务的多个操作必须在同一个连接下。

JDBC程序中为了让多个SQL语句作为一个事务执行:

  • 调用Connecation对象的setAutoCommit(false):以取消自动提交事务。
  • 在所有的SQL语句都成功执行后,调用commit()提交事务。
  • 在出现异常时,调用rollback()方法回滚事务

【案例:张三给李四转账】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.iweb.test;

import com.iweb.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestCommit {
public static void main(String[] args) {
Connection cn =null;
PreparedStatement ps=null;
try {
cn = JdbcUtils.getConnection();
// 开启事务
cn.setAutoCommit(false);
// 张三-500
ps=cn.prepareStatement("update account set money=money-? where name=?");
ps.setInt(1,500);
ps.setString(2,"zhangsan");
// 执行
ps.executeUpdate();

// 模拟错误 除非不能为0
System.out.println(1/0);

// 李四+500
ps=cn.prepareStatement("update account set money=money+? where name=?");
ps.setInt(1,500);
ps.setString(2,"lisi");
// 执行
ps.executeUpdate();
// 提交事务cn
cn.commit();
System.out.println("转账成功");
} catch (Exception e) {
System.out.println("转账失败");
// 事务回滚
try {
cn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally {
JdbcUtils.close(ps,cn);
}
}
}

这个部分灵活度很高,建议自己写代码,多调试几遍。话是什么说,但是还是多练吧

DAO结构

DAO:Data Access Object,数据访问对象。
项目文件标准文件结构(tree)

1
2
3
4
5
6
├───dao
│ ├───impl
│ └───interface
├───domain
├───service
└───utils

以上的各个文件夹的作用是:

  • dao:数据访问层,负责与数据库进行交互,执行SQL语句。
  • domain:领域模型层,负责定义业务模型。
  • service:业务逻辑层,负责处理业务逻辑。
  • utils:工具类层,负责提供一些常用的工具方法。
  • impl:实现类层,负责实现DAO接口中的方法。
  • interface:接口层,定义DAO接口。
  • test:测试类层,负责编写测试代码。

反射

反射是指在运行时动态获取类的信息并调用其方法的能力。

反射机制

java反射机制在程序的运行时动态加载类并获取类的详情信息,从而操作类或对象的属性和方法。其本质是JVM得到Class对象之后,从而获取对象的各种信息。

反射机制主要包括以下几个方面:

  1. 类加载机制:将类的字节码文件加载到内存中,形成类的Class对象。
  2. 反射API:提供了一组用于操作类、对象和方法的API。
  3. 动态代理:在运行时创建代理对象,用于拦截和修改方法的调用。

类加载

类加载是指将类的字节码文件加载到内存中,形成类的Class对象的过程。

Java文件通过java编译器(javac)将java源代码(.java)编译而生成的。编译器将java代码转换成字节码,存储在class文件中,class文件需要加载到虚拟机中之后才能运行和使用。而虚拟机如何加载class文件,class文件中的信息进入到虚拟机后发生什么变化,这个过程涉及到了java的类加载机制

类加载过程

主要由以下几个步骤组成:

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期可以概括为 7 个阶段:

加载、验证、准备、解析、初始化、使用和卸载。

1752645731436

1).加载(Loading)

加载是类加载过程的第一个阶段,在这个过程中,JVM会查找并加载类的二进制数据,通常通过类加载器(ClassLoader)完成。

类加载器:负责查找类文件,并将其加载到JVM中,JVM中内置了三个重要的ClassLoader:

  • 启动类加载器(Bootstrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 系统类加载器(System ClassLoader)

2).链接(Linking)

链接阶段分为三个子阶段:验证、准备和解析
画个流程图
加载—>验证—>准备—>解析—>初始化—>使用—>卸载

  • 验证(Verification):确保加载的类符合JVM规范,不会危害JVM的安全。
  • 准备(Preparation):为类的变量分配内存并设置默认初始化(int为0,引用对象为null)。
  • 解析(Resolution):将类、接口、字段和方法的符号引用转换为直接引用。

3).初始化(Initialization)

在初始化阶段,JVM会执行类中的静态初始化器和静态变量的赋值操作,初始化类变量和静态代码块。

4).使用(Using)

一旦类被初始化了,就可以被java程序使用了。可以通过new关键字创建类的实例。

5).卸载(Unloading)

当Java虚拟机结束时,或者在某个类不再被任务对象引用时,该类的类加载器可以卸载这个类。

1.2.2 双亲委派模型的核心机制

如果当一个类加载器收到类加载请求时,它不会先去加载,而是把这个请求委托父类加载器处理,如果父类加载器还存在其父类加载器,进一步向上委托,依次递归,最终将达到顶层的启动类加载器,如果父类加载器可以完成加载任务,就成功返回,倘如父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

class

class类是java反射机制的基础,反射机制主要是通过class类来实现的。

在java编程语言中,Class类是一个特殊的类,它用于表示JVM运行的类或者接口的信息。Class对象是在加载类时由Java虚拟机自动构建的,也就是不需要我们创建,JVM已经帮我们创建好了。

每个类在java中都对应一个Class对象,这个对象保存了该类的结构信息,比如类名、属性、方法等等。class类是一个反射工具,能提供很多的方法用于获取类的各种信息(构造方法,属性,方法,注解)。

获取类对象

java.lang.Class类的实例表示正在运行的Java应用程序中的类和接口。

获取类对象的方式(三种)

  1. Class.forName(“类的全路径名”)
  2. 类名.class
  3. 对象.getClass()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 1. Class.forName("类的全路径名")
    Class<?> c1 = Class.forName("com.bettfil.java快速学习速查.domain.Account");
    System.out.println(c1);
    // 2. 类名.class
    Class<Account> c2 = Account.class;
    System.out.println(c2);
    // 3. 对象.getClass()
    Account account = new Account();
    Class<? extends Account> c3 = account.getClass();
    System.out.println(c3);

Class类的常用方法

  • Field:获得对象的成员变量属性
  • Method:获得对象的方法
  • Constructor:获得对象的构造方法
    • getConstructors:获得对象的构造方法
  • Annotation:获得对象的注解
    • getAnnotations:获得对象的注解

获取属性值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test2 {

public static void main(String[] args) {
// 获取类对象
Class<Student> stuClass= Student.class;
// 获取属性
// getFields() 获得全部成员,包括继承而来的成员,不包括私有
// getDeclaredFields 获得本来定义的成员,包括私有,但是不包括继承而来
Field[] fields = stuClass.getDeclaredFields();
for(Field field : fields){
// getModifiers() 修饰符都是通过数字编号获取,要想获取具体内容
// filed.getType() 返回属性类型
// field.getName() 获得属性名
System.out.println(field.getModifiers()+"\t"+field.getType()+"\t"+field.getName());
}
}
}

如何看出来这个部分是反射呢?从哪个地方看出来的,有什么好处吗?

  • 从类对象中获取属性,这个类对象是我们自己定义的,我们可以通过类对象获取到类中的属性。
  • 反射机制的灵活性,我们可以在运行时动态地获取类的信息,包括属性、方法、构造函数等。

获取方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test3 {
public static void main(String[] args) {

try {
Class stuClass = Class.forName("com.iweb.bean.Student");
// getMethods() 获取类中定义的方法和继承而来的方法
// getDeclaredMethods() 获取本类定义的方法,包括私有,但是不包括继承而来
Method[] methods = stuClass.getDeclaredMethods();
for(Method method : methods){
System.out.println(method);

}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

获取构造函数

1
2
3
4
5
6
7
8
9
10
11
12
public class Test4 {
public static void main(String[] args) {
Student student=new Student();
Class<? extends Student> studentClass = student.getClass();
// getConstructors() 获取所有公开的构造方法
// getDeclaredConstructors() 获取所有构造方法(包括私有)
Constructor<?>[] constructors = studentClass.getDeclaredConstructors();
for(Constructor con :constructors){
System.out.println(con);
}
}
}

获得注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test5 {

public static void main(String[] args) {
try {
Class stuClass = Class.forName("com.iweb.bean.Student");
Annotation[] annotations = stuClass.getAnnotations();
for(Annotation anno: annotations){
System.out.println(anno);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

1.5 反射应用

Constructor构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.iweb.test;

public class Test6 {

public static void main(String[] args) {
try {
Class<?> stuClass = Class.forName("com.iweb.bean.Student");
// newInstance()表示调用这个类的无参构造函数,这个类没有无参构造方法,一定会出现异常
stuClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}
  • 指定参数的构造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Test6 {

public static void main(String[] args) {
method2();
}

public static void method1(){
try {
Class<?> stuClass = Class.forName("com.iweb.bean.Student");
// 调用有两个参数的构造方法,并进行赋值
// 需要申明参数的类型
Constructor<?> constructor = stuClass.getConstructor(String.class, int.class);
// 利用newInstance创建Student对象
Student stu = (Student)constructor.newInstance("zhangsan", 21);
System.out.println(stu);
} catch (Exception e) {
e.printStackTrace();
}
}

public static void method2(){
try {
Class<?> stuClass = Class.forName("com.iweb.bean.Student");
// 调用有两个参数的构造方法,并进行赋值
Constructor<?> constructor = stuClass.getDeclaredConstructor(String.class);
// 如果要访问私有的成员,不能直接使用
// 如果用反射强行获取并使用,需要取消访问检查(暴力访问,忽略访问权限修饰符)
constructor.setAccessible(true);
Student stu = (Student)constructor.newInstance("tom");
System.out.println(stu);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Field

反射获取成员并使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void method3(){
try {
Class<?> stuClass = Class.forName("com.iweb.bean.Student");
// 获得 name属性
Field field = stuClass.getDeclaredField("name");
// 通过反射创建对象
Student stu= (Student) stuClass.newInstance();
// 暴力访问
field.setAccessible(true);
// 有了对象才可以给指定对象进行赋值
field.set(stu,"jack");
// 获取指定对象的name值
System.out.println(field.get(stu));
} catch (Exception e) {
e.printStackTrace();
}
}

Method

调用本来的方法,一定要保证实例化对象,可以直接利用class类反射实例对象,反射调用方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void method4(){
try {
Class<?> stuClass = Class.forName("com.iweb.bean.Student");
// 获得student类里面的setName方法,需要传入类型字节码对象
Method method = stuClass.getDeclaredMethod("setName", String.class);
// 创建一个student对象,当作方法的调用者
Student stu= (Student) stuClass.newInstance();
// 运行方法
method.invoke(stu,"tom");
/* Field field = stuClass.getDeclaredField("name");
// 暴力访问
field.setAccessible(true);
System.out.println(field.get(stu));*/
} catch (Exception e) {
e.printStackTrace();
}
}

调用有返回值有参数的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void method5(){
try {
Class<?> stuClass = Class.forName("com.iweb.bean.Student");
// 获得student类里面的setName方法,需要传入类型字节码对象
Method method = stuClass.getDeclaredMethod("test2", String.class);
// 创建一个student对象,当作方法的调用者
Student stu= (Student) stuClass.newInstance();
// 运行方法,接受返回值
Object result = method.invoke(stu, "helloWorld");
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}

==总结==

在程序运行时,动态的加载类的信息(方法,属性,构造函数,注解),从而操作类或对象的属性和方法的这种机制称为Java反射。

实际案例

要求如下:
1.创建一个Emp类,定义私有的编码,私有的姓名和公开的年龄,3个属性,并创建set/get和toString方法,
添加无参构造,1个参数构造,2个参数构造赋值。
添加无参数无返回的study()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Emp {
private String code; // 私有编码
private String name; // 私有姓名
public int age; // 公开年龄

// 无参构造
public Emp() {
}

// 1个参数构造(年龄)
public Emp(int age) {
this.age = age;
}

// 2个参数构造(编码、姓名)
public Emp(String code, String name) {
this.code = code;
this.name = name;
}

// study方法
public void study() {
System.out.println(name + " is studying");
}

// getter和setter
public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Emp{code='" + code + "', name='" + name + "', age=" + age + "}";
}
}

2.测试以上三种获得类对象的方法
(1)通过类拿到字节码对象
new Emp().getClass()
(2)通过类的class属性拿到字节码对象
Emp.Class
(3) 通过类的路径拿到字节码对象
Class.forName()
3.获得类属性的方法。
(1)获得所有共有属性
(2)获得本类之中的属性
(3)为私有属性赋值,以及获取值。
4.利用反射创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.reflect.Constructor;

public class ObjectCreationTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Emp.class;

// 方式1:通过无参构造创建
Emp emp1 = (Emp) clazz.newInstance();
emp1.setCode("E003");
emp1.setName("王五");
emp1.age = 22;
System.out.println(emp1);

// 方式2:通过有参构造创建
Constructor<?> constructor = clazz.getConstructor(String.class, String.class);
Emp emp2 = (Emp) constructor.newInstance("E004", "赵六");
emp2.age = 23;
System.out.println(emp2);
}
}

5.利用反射执行study方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Method;

public class MethodInvocationTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Emp.class;
Emp emp = new Emp("E005", "钱七");
emp.age = 24;

// 获取study方法并执行
Method studyMethod = clazz.getMethod("study");
studyMethod.invoke(emp); // 输出:钱七 is studying

// 带参数方法示例(假设存在work方法)
// Method workMethod = clazz.getMethod("work", String.class);
// workMethod.invoke(emp, "Java开发");
}
}

注解

Java注解(Annotation)是JDK引入的一种注释机制,注解可以作用在类,属性,方法上等。注解可以做到不改变改变逻辑的前提下在代码中嵌入补充信息。

注解本质就是一个接口。

元注解

用来标注注解的注解叫做元注解(JDK的内置注解):

@Target:设置注解作用的位置:

  1. type
  2. field
  3. method

@Retention:设置注解作用的时机:

  1. RetentionPolicy.RUNTIME:运行时(大多数情况使用它)
  2. RetentionPolicy.CLASS:注解在字节码文件中存在
  3. RetentionPolicy.SOURCE:注解仅存在源码中

Junit

测试分类

用Java写完程序之后需要测试准确性,然后每一次测试如果出现错误,就需要重新修改重新写,这是一个比较麻烦的过程。

黑盒测试:不需要写代码,输入值,看程序是否能够输出期望的值。

白盒测试:需要写代码,关注程序的具体执行流程。Junit就是一个百盒测试的工具。

Junit概述

Junit是java编程语言的单元测试工具,是一个非常重要的测试工具(白盒测试)

特点

  • Junit是一个开放源代码的测试工具
  • 提供注解来识别测试方法
  • Junit测试可以让你编写代码更快,并提供之质量
  • Junit在一个进度条中显示进度,如果运行良好则是绿色,如果运行失败,则是红色

使用步骤

步骤

1.将Junit的jar包导入到工程中

2.编写测试方法必须是共同的无参数无返回的测试方法。

3.在测试方面上使用@Test注解来标识该方法是一个测试方法。

4.选中测试方法右键通过Junit运行该方法

1
2
3
4
5
6
7
public class Test7 {

@Test
public void test1(){
System.out.println(1/0);
}
}

相关注解[应用]

注解 含义
@Test 测试方法
@Before 在测试方法之前运行
@After 在测试方法之后运行

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.iweb.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class Test7 {

// 测试代码之前执行,用于初始化操作
@Before
public void before(){
System.out.println("before");
}

@Test
public void test1(){
System.out.println("test1");
}

// 在测试代码之后执行,用于释放资源
@After
public void after(){
System.out.println("after");
}
}

JDK8新特性

当前的java市场对JDK8和JDK9是主流,至于更高的11,17等,在企业中使用的最多的是JDK8和JDK9。